﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="InstallConfiguration.cs" company="Irdeto">
//   Copyright @ Irdeto 2012
// </copyright>
// <summary>
//   Class:InstallConfiguration
//   Namespace:AOTT.GateWay.WebInstaller
//   Class Funtion:Represent the install function
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace BasicFrameworkDemo.WCFInstaller
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Security.AccessControl;
    using System.Security.Principal;
    using System.ServiceProcess;
    using System.Xml;

    using Microsoft.Web.Administration;

    using NetFwTypeLib;

    /// <summary>
    /// Class:InstallConfiguration
    /// Namespace:AOTT.GateWay.WebInstaller
    /// Class Funtion:Represent the install function 
    /// </summary>
    /// Creator:zhouchao
    /// Creation Date:8/9/2011 1:24 PM
    /// Modifier:
    /// Last Modified:
    /// ----------------------------------------------------------------------------------------
    public class InstallConfiguration
    {
        /// <summary>
        /// Adds the directory account security.
        /// </summary>
        /// <param name="directory">The directory of account</param>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 4:41 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static void AddDirectoryAccountSecurity(string directory)
        {
            var dirInfo = new DirectoryInfo(directory);
            if (!dirInfo.Exists)
            {
                return;
            }

            var accessControl = Directory.GetAccessControl(directory, AccessControlSections.All);
            var accountList = (
                from FileSystemAccessRule rule
                in accessControl.GetAccessRules(true, true, typeof(NTAccount))
                where rule.IdentityReference.Value.Contains("Administrators")
                select new
                {
                    name = rule.IdentityReference.Value,
                    identity = rule.IdentityReference,
                }).ToList();
            if (accountList.Count == 0)
            {
                return;
            }

            var user = accountList.First().name.Replace("Administrators", "IIS_IUSRS");
            try
            {
                accessControl.SetAccessRule(new FileSystemAccessRule(user, FileSystemRights.ListDirectory | FileSystemRights.Read | FileSystemRights.ReadAndExecute | FileSystemRights.Modify, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
                dirInfo.SetAccessControl(accessControl);
            }
            catch (Exception)
            {
                return;
            }
        }

        /// <summary>
        /// Creates the web site.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <param name="port">The port</param>
        /// <param name="path">The path</param>
        /// <param name="allowDirectoryBrowse">The  allowDirectoryBrowse indicates whether</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/9/2011 1:24 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static string CreateWebSite(string siteName, string port, string path, bool allowDirectoryBrowse)
        {
            try
            {
                var sitePort = Convert.ToInt32(port);

                // /create the application of the name for siteName pool.
                var message = CreateAppPool(siteName);
                if (!string.IsNullOrEmpty(message))
                {
                    return message;
                }

                var bindArgs = string.Format("*:{0}:", sitePort);

                // /Validate sitename and port
                using (var serverManager = new ServerManager())
                {
                    if (serverManager.Sites.Any(site => site.Name == siteName))
                    {
                        return string.Format("The name of web site for {0} already exists.", siteName);
                    }

                    if (serverManager.Sites.Any(site => site.Bindings.Any(binding => binding.BindingInformation.TrimStart('*') == string.Format(":{0}:", port))))
                    {
                        return string.Format("{0} ports have been occupied.", sitePort);
                    }
                }

                // /create the name of web site for siteName.
                using (var serverManager = new ServerManager())
                {
                    serverManager.Sites.Add(siteName, "http", bindArgs, path);
                    var site = serverManager.Sites[siteName];
                    site.ServerAutoStart = true;
                    site.Applications["/"].ApplicationPoolName = siteName;
                    serverManager.CommitChanges();
                }

                if (allowDirectoryBrowse)
                {
                    AddDirectoryBrowse(siteName);
                }

                AddRunAllManagedModulesForAllRequests(siteName);
                AddAuthorization(siteName);
                OpenPort(siteName, sitePort);
                return string.Empty;
            }
            catch (Exception exception)
            {
                return exception.Message;
            }
        }

        /// <summary>
        /// Deletes the web site.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <param name="port">The port</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/9/2011 1:24 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static string DeleteWebSite(string siteName, string port)
        {
            try
            {
                using (var serverManager = new ServerManager())
                {
                    if (serverManager.Sites.Any(sites => sites.Name == siteName))
                    {
                        var site = serverManager.Sites[siteName];
                        serverManager.Sites.Remove(site);
                        serverManager.CommitChanges();
                    }
                }

                ClosePort(Convert.ToInt32(port));

                // /delete the application of the name for siteName pool.
                return DeleteAppPool(siteName);
            }
            catch (Exception exception)
            {
                return exception.Message;
            }
        }

        /// <summary>
        /// Adds the state of the session.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// Creator:zhouchao
        /// Creation Date:8/18/2011 11:41 AM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static void AddSessionState(string siteName)
        {
            try
            {
                using (var serverManager = new ServerManager())
                {
                    var webConfig = serverManager.GetWebConfiguration(siteName);
                    var directoryBrowse = webConfig.GetSection("system.web/sessionState");
                    directoryBrowse.SetAttributeValue("mode", "StateServer");
                    directoryBrowse.SetAttributeValue("stateConnectionString", "tcpip=127.0.0.1:42424");
                    directoryBrowse.SetAttributeValue("cookieless", "AutoDetect");

                    // /directoryBrowse.SetAttributeValue("timeout", "60");
                    serverManager.CommitChanges();
                }

                using (var service = new ServiceController("aspnet_state"))
                {
                    if (service.Status != ServiceControllerStatus.Running)
                    {
                        service.Start();
                    }
                }
            }
            catch
            {
                return;
            }
        }

        /// <summary>
        /// Updates the handler section.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/9/2011 3:17 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static string AddExtensionlessUrl(string siteName)
        {
            using (var serverManager = new ServerManager())
            {
                var webConfig = serverManager.GetWebConfiguration(siteName);
                var handlersSection = webConfig.GetSection("system.webServer/handlers");
                var handles = new List<string>
                                      {
                                          "ExtensionlessUrl-Integrated-4.0",
                                          "ExtensionlessUrl-ISAPI-4.0_32bit", 
                                          "ExtensionlessUrl-ISAPI-4.0_64bit"
                                      };
                var fileList = new List<string> { "*.", ".svc" };
                var collection = handlersSection.GetCollection().Where(handle => handles.Contains(Convert.ToString(handle.GetAttributeValue("name")), StringComparer.OrdinalIgnoreCase) && fileList.Contains(Convert.ToString(handle.GetAttributeValue("path")), StringComparer.OrdinalIgnoreCase)).ToList();
                try
                {
                    foreach (var handle in collection)
                    {
                        handle.SetAttributeValue("verb", "GET,HEAD,POST,DEBUG,PUT,DELETE");
                    }

                    serverManager.CommitChanges();
                    return string.Empty;
                }
                catch (Exception exception)
                {
                    return exception.Message;
                }
            }
        }

        /// <summary>
        /// Adds the default document.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <param name="defaultDocName">Default name of the doc.</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 2:10 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static string AddDefaultDocument(string siteName, string defaultDocName)
        {
            using (var serverManager = new ServerManager())
            {
                var webConfig = serverManager.GetWebConfiguration(siteName);
                var defaultDocumentSection = webConfig.GetSection("system.webServer/defaultDocument");
                var element = defaultDocumentSection.GetChildElement("files");
                var collection = element.GetCollection();
                if (collection.Any(file => Convert.ToString(file.GetAttributeValue("value")) == defaultDocName))
                {
                    return string.Empty;
                }

                try
                {
                    var docElement = collection.CreateElement();
                    docElement.SetAttributeValue("value", defaultDocName);
                    collection.Add(docElement);
                    serverManager.CommitChanges();
                    return string.Empty;
                }
                catch (Exception exception)
                {
                    return exception.Message;
                }
            }
        }

        /// <summary>
        /// Adds the app settings.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <param name="key">The key of node of appsettings</param>
        /// <param name="value">The value of node of appsettings</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 6:34 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static string AddAppSettings(string siteName, string key, string value)
        {
            using (var serverManager = new ServerManager())
            {
                var webConfig = serverManager.GetWebConfiguration(siteName);
                var appSettingsSection = webConfig.GetSection("appSettings");
                var collection = appSettingsSection.GetCollection();
                try
                {
                    var appSettings = collection.Where(attribute => Convert.ToString(attribute.GetAttributeValue("key")) == key).ToList();
                    if (appSettings.Count > 0)
                    {
                        var docElement = appSettings.First();
                        docElement.SetAttributeValue("value", value);
                    }
                    else
                    {
                        var docElement = collection.CreateElement();
                        docElement.SetAttributeValue("key", key);
                        docElement.SetAttributeValue("value", value);
                        collection.Add(docElement);
                    }

                    serverManager.CommitChanges();
                    return string.Empty;
                }
                catch (Exception exception)
                {
                    return exception.Message;
                }
            }
        }

        /// <summary>
        /// Adds the connection strings.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <param name="name">The name of node of connectionStrings</param>
        /// <param name="connectionString">The connectionString of node of connectionStrings</param>
        /// <param name="providerName">Name of the provider.</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/25/2011 3:44 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static string AddConnectionStrings(string siteName, string name, string connectionString, string providerName)
        {
            using (var serverManager = new ServerManager())
            {
                var webConfig = serverManager.GetWebConfiguration(siteName);
                var appSettingsSection = webConfig.GetSection("connectionStrings");
                var collection = appSettingsSection.GetCollection();
                try
                {
                    var appSettings = collection.Where(attribute => Convert.ToString(attribute.GetAttributeValue("name")) == name).ToList();
                    if (appSettings.Count > 0)
                    {
                        var docElement = appSettings.First();
                        docElement.SetAttributeValue("connectionString", connectionString);
                        if (string.IsNullOrEmpty(providerName))
                        {
                            docElement.SetAttributeValue("providerName", providerName);
                        }
                    }
                    else
                    {
                        var docElement = collection.CreateElement();
                        docElement.SetAttributeValue("name", name);
                        docElement.SetAttributeValue("connectionString", connectionString);
                        if (string.IsNullOrEmpty(providerName))
                        {
                            docElement.SetAttributeValue("providerName", providerName);
                        }

                        collection.Add(docElement);
                    }

                    serverManager.CommitChanges();
                    return string.Empty;
                }
                catch (Exception exception)
                {
                    return exception.Message;
                }
            }
        }

        /// <summary>
        /// Adds the service.
        /// </summary>
        /// <param name="webSitePath">The path of web site</param>
        /// <param name="address">The address of service</param>
        /// <param name="binding">The binding of service</param>
        /// <param name="bindingConfiguration">The bindingConfiguration of service</param>
        /// <param name="contract">The contract of service</param>
        /// <param name="name">The name of service</param>
        /// Creator:zhouchao
        /// Creation Date:8/1/2011 9:42 AM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        public static void AddServiceModel(string webSitePath, string address, string binding, string bindingConfiguration, string contract, string name)
        {
            var filePath = string.Format("{0}/web.config", webSitePath);
            var xmlDocument = CreateWebConfig(webSitePath);
            var configuration = xmlDocument.DocumentElement;
            if (configuration == null)
            {
                return;
            }

            var serviceModel = xmlDocument.CreateElement("system.serviceModel");
            var client = xmlDocument.CreateElement("client");
            var endpoint = xmlDocument.CreateElement("endpoint");
            endpoint.SetAttribute("address", address);
            endpoint.SetAttribute("binding", binding);
            endpoint.SetAttribute("bindingConfiguration", bindingConfiguration);
            endpoint.SetAttribute("contract", contract);
            endpoint.SetAttribute("name", name);
            client.AppendChild(endpoint);
            var serviceModels = configuration.Cast<XmlNode>().Where(element => element.Name == "system.serviceModel").ToList();
            if (serviceModels.Count == 0)
            {
                serviceModel.AppendChild(client);
                configuration.AppendChild(serviceModel);
                xmlDocument.Save(filePath);
                return;
            }

            var clients = serviceModels.First().Cast<XmlNode>().Where(element => element.Name == "client").ToList();
            if (clients.Count > 0)
            {
                serviceModels.First().RemoveChild(clients.First());
            }

            serviceModels.First().AppendChild(client);
            xmlDocument.Save(filePath);
        }

        /// <summary>
        /// Directories the browse.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 12:01 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static void AddDirectoryBrowse(string siteName)
        {
            try
            {
                using (var serverManager = new ServerManager())
                {
                    var webConfig = serverManager.GetWebConfiguration(siteName);
                    var directoryBrowse = webConfig.GetSection("system.webServer/directoryBrowse");
                    directoryBrowse.SetAttributeValue("enabled", true);
                    serverManager.CommitChanges();
                }
            }
            catch
            {
                return;
            }
        }

        /// <summary>
        /// Adds the run all managed modules for all requests.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 12:02 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static void AddRunAllManagedModulesForAllRequests(string siteName)
        {
            try
            {
                using (var serverManager = new ServerManager())
                {
                    var webConfig = serverManager.GetWebConfiguration(siteName);
                    var modules = webConfig.GetSection("system.webServer/modules");
                    modules.SetAttributeValue("runAllManagedModulesForAllRequests", true);
                    serverManager.CommitChanges();
                }
            }
            catch
            {
                return;
            }
        }

        /// <summary>
        /// Adds the default document.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 2:10 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static void AddAuthorization(string siteName)
        {
            using (var serverManager = new ServerManager())
            {
                var webConfig = serverManager.GetWebConfiguration(siteName);
                var authorizationSection = webConfig.GetSection("system.web/authorization");
                var authorizations = authorizationSection.GetCollection();
                if (authorizations.Any(authorization => Convert.ToString(authorization.GetAttributeValue("users")).ToLower() == "all" && authorization.ElementTagName.ToLower() == "allow"))
                {
                    return;
                }

                try
                {
                    var docElement = authorizations.CreateElement("allow");
                    docElement.SetAttributeValue("users", "All");
                    authorizations.Add(docElement);
                    serverManager.CommitChanges();
                }
                catch
                {
                    return;
                }
            }
        }

        /// <summary>
        /// Creates the app pool.
        /// </summary>
        /// <param name="appPoolName">Name of the app pool.</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/9/2011 1:24 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static string CreateAppPool(string appPoolName)
        {
            try
            {
                using (var serverManager = new ServerManager())
                {
                    ApplicationPool apppool;
                    if (!serverManager.ApplicationPools.Any(pool => pool.Name == appPoolName))
                    {
                        serverManager.ApplicationPools.Add(appPoolName);
                        apppool = serverManager.ApplicationPools[appPoolName];
                    }
                    else
                    {
                        apppool = serverManager.ApplicationPools[appPoolName];
                        if (apppool.State != ObjectState.Started)
                        {
                            apppool.Start();
                        }
                    }

                    apppool.ManagedPipelineMode = ManagedPipelineMode.Classic;
                    apppool.ManagedRuntimeVersion = "v4.0";
                    apppool.AutoStart = true;
                    serverManager.CommitChanges();

                    return string.Empty;
                }
            }
            catch (Exception exception)
            {
                return exception.Message;
            }
        }

        /// <summary>
        /// Deletes the app pool.
        /// </summary>
        /// <param name="appPoolName">Name of the app pool.</param>
        /// <returns>
        /// string.Empty, or exception.Message if an exception occurs.
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/10/2011 11:18 AM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static string DeleteAppPool(string appPoolName)
        {
            try
            {
                using (var serverManager = new ServerManager())
                {
                    if (!serverManager.ApplicationPools.Any(pool => pool.Name == appPoolName))
                    {
                        return string.Empty;
                    }

                    var apppool = serverManager.ApplicationPools[appPoolName];
                    if (apppool.State != ObjectState.Stopped)
                    {
                        apppool.Stop();
                    }

                    serverManager.ApplicationPools.Remove(apppool);
                    serverManager.CommitChanges();

                    return string.Empty;
                }
            }
            catch (Exception exception)
            {
                return exception.Message;
            }
        }

        /// <summary>
        /// Creates the web config.
        /// </summary>
        /// <param name="webSitePath">The path of web site</param>
        /// <returns>
        /// The XmlDocument
        /// </returns>
        /// Creator:zhouchao
        /// Creation Date:8/1/2011 9:36 AM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static XmlDocument CreateWebConfig(string webSitePath)
        {
            if (!Directory.Exists(webSitePath))
            {
                Directory.CreateDirectory(webSitePath);
            }

            var filePath = string.Format("{0}/web.config", webSitePath);
            var xmlDocument = new XmlDocument();
            if (File.Exists(filePath))
            {
                xmlDocument.Load(filePath);
            }
            else
            {
                var dec = xmlDocument.CreateXmlDeclaration("1.0", "utf-8", null);
                xmlDocument.AppendChild(dec);
            }

            var configuration = xmlDocument.DocumentElement;
            if (configuration == null)
            {
                xmlDocument.AppendChild(xmlDocument.CreateElement("configuration"));
                xmlDocument.Save(filePath);
            }

            return xmlDocument;
        }

        /// <summary>
        /// Opens the port.
        /// </summary>
        /// <param name="siteName">Name of the site.</param>
        /// <param name="port">The port</param>
        /// Creator:zhouchao
        /// Creation Date:8/15/2011 6:56 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static void OpenPort(string siteName, int port)
        {
            var netFwMgr = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwMgr"));
            var ports = (INetFwOpenPort)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwOpenPort"));
            ports.Name = siteName;
            ports.Port = port;
            ports.Enabled = true;
            ports.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
            netFwMgr.LocalPolicy.CurrentProfile.GloballyOpenPorts.Add(ports);
        }

        /// <summary>
        /// Closes the port.
        /// </summary>
        /// <param name="port">The port</param>
        /// Creator:zhouchao
        /// Creation Date:8/15/2011 6:59 PM
        /// Modifier:
        /// Last Modified:
        /// ----------------------------------------------------------------------------------------
        private static void ClosePort(int port)
        {
            var netFwMgr = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwMgr"));
            netFwMgr.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(port, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP);
        }
    }
}
